package com.zhike.service.file;

import java.io.IOException;
import java.util.HashMap;

import javax.annotation.Resource;
import javax.servlet.ServletException;

import org.liuxp.minioplus.api.StorageService;
import org.liuxp.minioplus.api.model.vo.FileMetadataInfoVo;
import org.liuxp.minioplus.common.exception.MinioPlusException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.pagehelper.util.StringUtil;
import com.google.gson.Gson;
import com.zhike.common.component.SnowflakeID;
import com.zhike.common.dto.MinioStorage;
import com.zhike.common.enums.ResultCode;
import com.zhike.common.exception.ZkimException;
import com.zhike.common.utils.FileHelper;
import com.zhike.service.store.Alioss;
import com.zhike.service.system.ISystemConfigService;
import com.zhike.utils.file.httpfile.FileUpload;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FileBase {
  /**
   * <p>
   * Apache文件上传组件在解析上传数据中的每个字段内容时，需要临时保存解析出的数据，以
   * 便在后面进行数据的进一步处理（保存在磁盘特定位置或插入数据库）。因为Java虚拟机默
   * 认可以使用的内存空间是有限的，超出限制时将会抛出“java.lang.OutOfMemoryError”错
   * 误。如果上传的文件很大，例如800M的文件，在内存中将无法临时保存该文件内容，Apache
   * 文件上传组件转而采用临时文件来保存这些数据；但如果上传的文件很小，例如600个字节的
   * 文件，显然将其直接保存在内存中性能会更加好些。
   * 
   * <p>
   * setSizeThreshold方法用于设置是否将上传文件已临时文件的形式保存在磁盘的临界值（以
   * 字节为单位的int值），如果从没有调用该方法设置此临界值，将会采用系统默认值10KB。对
   * 应的getSizeThreshold() 方法用来获取此临界值。
   */
  public static final int THRESHOLD_SIZE = 1024 * 200;// 200KB //1024 * 1024 * 3; // 3MB
  public static final int MAX_FILE_SIZE = 1024 * 5000;// 500;// 500KB //1024 * 1024 * 200; // 200MB
  public static final int MAX_REQUEST_SIZE = MAX_FILE_SIZE; // 1024 * 1024 * 200; // 200MB

  @Resource
  private FileUpload fileUpload;

  @Autowired
  private StorageService storageService;

  @Autowired
  private ISystemConfigService systemConfigService;

  @Autowired
  private SnowflakeID snowflakeID;

  @Autowired
  private Alioss alioss;

  /**
   * 处理上传
   * 
   * @param uploader
   * @param file
   * @param ids      是可变参数，第1个表示用户ID，第2个表示群ID
   * @return
   * @throws ServletException
   * @throws IOException
   */
  public HashMap<String, Object> commonUploader(UploaderRoot uploader, MultipartFile file, String... ids)
      throws ServletException, IOException {

    log.debug("[" + uploader.getDebugTAG() + "] 正在处理用户的文件上传请求 ...............");
    HashMap<String, Object> parametersFromClient = new HashMap<>();
    try {
      log.debug(" 正在处理用户的文件上传请求 ...............");
      String storage = systemConfigService.getValueByKey("storage", "default");
      if (storage.equals("minio")) {
        FileMetadataInfoVo vo = storageService.createFile(file.getOriginalFilename(), false, ids[0],
            file.getInputStream());
        if (vo != null) {
          parametersFromClient.put(FileUpload.SUCESS_KEY_NAME, Boolean.TRUE);
          parametersFromClient.put("file_name", vo.getFileKey());
          parametersFromClient.put("user_uid", ids[0]);
          if (ids.length == 2) {
            parametersFromClient.put("group_id", ids[1]);
          }

          String filePath = "/" + vo.getStorageBucket() + "/" + vo.getStoragePath() +
              "/" + vo.getFileMd5();
          String minioJsonStr = systemConfigService.getValueByKey("storage", "minio");
          if (StringUtils.isNotBlank(minioJsonStr)) {
            MinioStorage minioStorage = new Gson().fromJson(minioJsonStr, MinioStorage.class);
            if (null != minioStorage) {
              String protocol = minioStorage.getSsl() ? "https://" : "http://";
              String domain = protocol + minioStorage.getDomain();
              parametersFromClient.put("url", domain + filePath);
              parametersFromClient.put("downloadUrl", storageService.download(vo.getFileKey(), ids[0]));
            }
          }

        } else {
          parametersFromClient.put(FileUpload.SUCESS_KEY_NAME, Boolean.FALSE);
        }

      } else if (storage.equals("aliyun")) {
        String filename = snowflakeID.nextIdStr();
        String url = alioss.upload(file, uploader.getPathTag(), filename);
        log.debug("文件上传成功 url: {}", url);
        if (StringUtils.isNotBlank(url)) {
          parametersFromClient.put(FileUpload.SUCESS_KEY_NAME, Boolean.TRUE);
          parametersFromClient.put("file_name", FileHelper.extractPath(url));
          parametersFromClient.put("user_uid", ids[0]);
          if (ids.length == 2) {
            parametersFromClient.put("group_id", ids[1]);
          }
          parametersFromClient.put("url", url);
        }
      } else {
        // 此方法目前是通用的文件上传处理API，默认是支持多文件上传的，目前是假定客户端只传一个文件，服务端其实并没有做检查，实际应用时不会！
        parametersFromClient = fileUpload.processFileUpload2(uploader,
            file, ids[0], THRESHOLD_SIZE, MAX_FILE_SIZE, MAX_REQUEST_SIZE);
      }

      // 文件上传处理成功
      if ((Boolean) parametersFromClient.get(FileUpload.SUCESS_KEY_NAME)) {
        uploader.onUploadSucess(parametersFromClient);
      }

    } catch (MinioPlusException e) {
      log.error("{}", e.getErrorMessage());
      throw new ZkimException(ResultCode.SYS_FILE_WRITE);
    } catch (Exception ex) {
      ex.printStackTrace();
      log.error("处理上传时出错了{}", ex.getMessage());
      throw new ZkimException(ResultCode.OTHER_ERROR);
    }
    return parametersFromClient;
  }

}
